﻿//---------------------------------------------------------------------------------
// Copyright 2011 Microsoft Corporation
// Licensed under the Microsoft Public License (MS-PL) (the "License"); 
// You may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.opensource.org/licenses/ms-pl.html
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR 
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
// INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR 
// CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
// MERCHANTABLITY OR NON-INFRINGEMENT. 
// See the Microsoft Public License (MS-PL) for the specific language governing 
// permissions and limitations under the License.
//---------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Services.Client;
using System.Threading;

namespace Microsoft.Telco.WindowsLive
{
	public class LiveDataServiceCollection<T> : DataServiceCollection<T>
	{
		// Fields
		private object asyncState;
		private LiveDataContext context;
		private int isLoading;
		private IEnumerable<T> items;
		private SynchronizationContext syncContext;

		// Events
		public event EventHandler<AsyncCompletedEventArgs> LoadCompleted;

		// Methods
		internal LiveDataServiceCollection(LiveResource parent)
			: base(null, TrackingMode.None)
		{
			this.Owner = parent;
		}

		public LiveDataServiceCollection(LiveDataContext context, IEnumerable<T> items)
			: base(context)
		{
			this.context = context;
			this.items = items;
		}

		private void CheckLoadInProgress()
		{
			if (Interlocked.CompareExchange(ref this.isLoading, 1, 0) == 1)
			{
				throw new InvalidOperationException(StringResource.LoadInProgress);
			}
		}

		public new DataServiceQuery<T> GetQuery()
		{
			if ((this.items != null) && (this.items is DataServiceQuery<T>))
			{
				return (this.items as DataServiceQuery<T>);
			}
			return this.DataContext.CreateRelatedQuery<T>(this.Owner, this);
		}

		public void Load()
		{
			if (this.items == null)
			{
				this.items = this.GetQuery();
			}
			this.Load(this.items);
		}

		public new void Load(IEnumerable<T> items)
		{
			this.CheckLoadInProgress();
			this.items = items;
			base.Load(this.items);
			Interlocked.Exchange(ref this.isLoading, 0);
		}

		public void LoadAsync(object state)
		{
			this.LoadAsync(null, state);
		}

		public void LoadAsync(IEnumerable<T> items, object state)
		{
			if (items != null)
			{
				this.items = items;
			}
			DataServiceQuery<T> query = null;
			if (this.items != null)
			{
				query = this.items as DataServiceQuery<T>;
			}
			else
			{
				query = this.GetQuery();
			}
			if (query == null)
			{
				throw new InvalidOperationException("Collection is not associated with a query. Use Load() for loading in-memory collections.");
			}
			this.CheckLoadInProgress();
			this.asyncState = state;
			this.syncContext = SynchronizationContext.Current;
			query.BeginExecute(delegate(IAsyncResult result)
			{
				if (result.IsCompleted)
				{
					Exception error = null;
					try
					{
						QueryOperationResponse<T> items2 = query.EndExecute(result) as QueryOperationResponse<T>;
						if (items != null)
						{
							((LiveDataServiceCollection<T>)this).Load(items2);
							((LiveDataServiceCollection<T>)this).Continuation = items2.GetContinuation();
						}
					}
					catch (Exception exception2)
					{
						error = exception2;
					}
					finally
					{
						((LiveDataServiceCollection<T>)this).OnLoadCompleted(error);
					}
				}
			}, null);
		}

		public void LoadNextPartialSet()
		{
			if (base.Continuation != null)
			{
				base.Load(this.DataContext.Execute<T>(base.Continuation));
			}
		}

		public void LoadNextPartialSetAsync(object state)
		{
			this.CheckLoadInProgress();
			this.asyncState = state;
			this.syncContext = SynchronizationContext.Current;
			if (base.Continuation != null)
			{
				this.OnLoadCompleted(null);
			}

			this.DataContext.BeginExecute<T>(base.Continuation, delegate(IAsyncResult result)
			{
				if (result.IsCompleted)
				{
					Exception error = null;
					try
					{
						QueryOperationResponse<T> items = DataContext.EndExecute<T>(result) as QueryOperationResponse<T>;
						if (items != null)
						{
							base.Load(items);
							base.Continuation = items.GetContinuation();
						}
					}
					catch (Exception exception2)
					{
						error = exception2;
					}
					finally
					{
						OnLoadCompleted(error);
					}
				}
			}, null);
		}

		private void OnLoadCompleted(Exception error)
		{
			SendOrPostCallback d = null;
			WaitCallback callBack = null;
			Interlocked.Exchange(ref this.isLoading, 0);
			if (this.LoadCompleted != null)
			{
				if (this.syncContext != null)
				{
					if (d == null)
					{
						d = delegate
						{
							((LiveDataServiceCollection<T>)this).LoadCompleted((LiveDataServiceCollection<T>)this, new AsyncCompletedEventArgs(error, false, ((LiveDataServiceCollection<T>)this).asyncState));
						};
					}
					this.syncContext.Post(d, null);
				}
				else
				{
					if (callBack == null)
					{
						callBack = delegate
						{
							((LiveDataServiceCollection<T>)this).LoadCompleted((LiveDataServiceCollection<T>)this, new AsyncCompletedEventArgs(error, false, ((LiveDataServiceCollection<T>)this).asyncState));
						};
					}
					ThreadPool.QueueUserWorkItem(callBack);
				}
			}
		}

		// Properties
		internal LiveDataContext DataContext
		{
			get
			{
				if ((this.context == null) && (this.Owner != null))
				{
					this.context = this.Owner.GetDataContext();
				}
				return this.context;
			}
		}

		public LiveResource Owner { get; set; }

	}

}